package commons.internal.configuration;

import static commons.Preconditions.check;
import static commons.Preconditions.checkNotNull;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import commons.configuration.Configuration;
import commons.exception.InvalidConfigurationException;

/**
 * This is the Configuration implementation for the file system server. The
 * valid file for this configuration has the following structure:<br\>
 * -name=value<br\>
 * -comments should start with #<br\>
 * 
 * @author Armstrong Mardilson da Silva Goes
 * 
 */
public class ServerConfiguration implements Configuration {

	/*
	 * All the comments lines should start with this value
	 */
	private static final String			commentsHead			= "#";

	/*
	 * The parameters values must be separated from its names with this value
	 */
	private static final String			nameValueSeparator		= "=";

	/**
	 * The directory where the {@code StorageUnit} are stored.
	 */
	public static final String			storageDeviceDirectory	= "Storage.Directory";

	/**
	 * The max amount of data which can be transferred on each writing in the
	 * network.
	 */
	public static final String			transferMaxBufferSize	= "MaxBufferSize";

	private final Map<String, String>	parametersMap;
	private Scanner						fileScanner;

	public ServerConfiguration() {

		parametersMap = new HashMap<String, String>();
	}

	private boolean allTheParametersValuesAreValid(
			final Map<String, String> configMap) {
		final Collection<String> values = configMap.values();
		boolean allValuesValid = true;

		for (final String value : values) {
			allValuesValid &= !value.isEmpty();
		}

		return allValuesValid;
	}

	private boolean containsAllTheRequiredParameters(
			final Map<String, String> configMap) {
		final Set<String> names = configMap.keySet();
		return names.contains(storageDeviceDirectory)
				&& names.contains(transferMaxBufferSize);
	}

	@Override
	public String getConfigurationParameter(final String parameter) {
		checkNotNull("parameter must not be null.", parameter);
		return parametersMap.get(parameter);
	}

	@Override
	public void load(final String fileName)
			throws InvalidConfigurationException, IOException {

		checkNotNull("fileName must not be null.", fileName);

		final File configurationFile = new File(fileName);

		check(fileName + " does not exist.", configurationFile.exists());
		check(fileName + " cannot be read.", configurationFile.canRead());

		final Map<String, String> mapContent = readParametersFromFile(configurationFile);

		if (!containsAllTheRequiredParameters(mapContent)) {
			throw new InvalidConfigurationException(
					"Some parameters are missing.");
		}

		if (!allTheParametersValuesAreValid(mapContent)) {
			throw new InvalidConfigurationException(
					"There are invalid parameters values.");
		}

		parametersMap.putAll(mapContent);
	}

	private String readNextNotCommentedLine() {
		String line = null;
		while (fileScanner.hasNext()) {
			line = fileScanner.nextLine();
			if (!line.startsWith(commentsHead) && !line.isEmpty()) {
				return line;
			}
		}
		return null;
	}

	private Map<String, String> readParametersFromFile(
			final File configurationFile) throws InvalidConfigurationException,
			FileNotFoundException {
		fileScanner = new Scanner(configurationFile);
		final Map<String, String> auxMap = new HashMap<String, String>();
		String fileLine = readNextNotCommentedLine();

		while (fileLine != null) {

			final String[] tokens = fileLine.split(nameValueSeparator);

			if (tokens.length != 2) {
				throw new InvalidConfigurationException(
						"Invalid configuration format.");
			}

			auxMap.put(tokens[0], tokens[1]);

			fileLine = readNextNotCommentedLine();
		}
		return auxMap;
	}
}
